﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Printing;
using System.Windows.Resources;
using System.Windows.Shapes;
using System.Xml.Linq;
using Microsoft.LightSwitch.Presentation;
using System.Text; 

namespace SilverlightTextEditor
{
    public partial class PageDescription : UserControl
    {
        public static readonly DependencyProperty DummyDependencyProperty =
            DependencyProperty.Register("DummyProperty", typeof(IContentItem),
            typeof(PageDescription),
            new PropertyMetadata(OnRichTextPropertyPropertyChanged));

        public PageDescription()
        {
            InitializeComponent();
            this.SetBinding(DummyDependencyProperty, new Binding());
        }

        private static void OnRichTextPropertyPropertyChanged(
            DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            // Get the DataContext
            PageDescription objMainPage = (PageDescription)d;
            IContentItem contentItem = (IContentItem)objMainPage.DataContext;

            // Create a handler that will fire whenever the value we are bound to
            // in LightSwitch changes
            contentItem.PropertyChanged +=
                new System.ComponentModel.PropertyChangedEventHandler(objMainPage.contentItem_PropertyChanged);
        }

        private void contentItem_PropertyChanged(object sender, 
            System.ComponentModel.PropertyChangedEventArgs e)
        {
            // Get the DataContext
            IContentItem contentItem = (IContentItem)sender;

            // Make sure the contentItem is not null
            if (contentItem.Value != null)
            {
                // Make sure we do not already have the value set 
                // from rtb_ContentChanged to prevent endless looping
                if (contentItem.StringValue != rtb.Xaml)
                {
                    // Check to see if the contentItem is an empty string
                    if (contentItem.StringValue != "")
                    {
                        // Set the value on the RichTextControl
                        try
                        {
                            rtb.Xaml = contentItem.StringValue;                            
                        }
                        catch (Exception)
                        {
                            StringBuilder SB = new StringBuilder();
                            SB.Append("<Section xml:space='preserve' HasTrailingParagraphBreakOnPaste='False' ");
                            SB.Append("xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'>");
                            SB.Append("<Paragraph FontSize='20' FontFamily='Calibri' Foreground='#FF000000' ");
                            SB.Append("FontWeight='Normal' FontStyle='Normal' FontStretch='Normal' ");
                            SB.Append("TextAlignment='Left'><Run Text='' /></Paragraph></Section>");

                            rtb.Xaml = SB.ToString();
                        }
                        xamlTb.Text = contentItem.StringValue;
                        
                    }
                    else
                    {
                        // The RichText Control will throw an error 
                        // if you attempt to set it to an empty string
                        // Create and set the minimal default text

                        StringBuilder SB = new StringBuilder();
                        SB.Append("<Section xml:space='preserve' HasTrailingParagraphBreakOnPaste='False' ");
                        SB.Append("xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'>");
                        SB.Append("<Paragraph FontSize='20' FontFamily='Calibri' Foreground='#FF000000' ");
                        SB.Append("FontWeight='Normal' FontStyle='Normal' FontStretch='Normal' ");
                        SB.Append("TextAlignment='Left'><Run Text='' /></Paragraph></Section>");

                        rtb.Xaml = SB.ToString();
                    }
                }
            }
            else
            {
                // The RichText Control will throw an error 
                // if you attempt to set it to an empty string
                // Create and set the minimal default text

                StringBuilder SB = new StringBuilder();
                SB.Append("<Section xml:space='preserve' HasTrailingParagraphBreakOnPaste='False' ");
                SB.Append("xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'>");
                SB.Append("<Paragraph FontSize='20' FontFamily='Calibri' Foreground='#FF000000' ");
                SB.Append("FontWeight='Normal' FontStyle='Normal' FontStretch='Normal' ");
                SB.Append("TextAlignment='Left'><Run Text='' /></Paragraph></Section>");

                rtb.Xaml = SB.ToString();
            }
        }

        private void rtb_ContentChanged(object sender, ContentChangedEventArgs e)
        {
            // Get the DataContext
            IContentItem contentItem = (IContentItem)this.DataContext;

            // Make sure we do not already have the value set 
            // from contentItem_PropertyChanged to prevent endless looping
            if (contentItem.StringValue != rtb.Xaml)
            {
                // Set the value of the RichTextControl
                // to the value in LightSwitch
                contentItem.Value = rtb.Xaml;
            }
        }

        #region Bold, Italics & Underline

        //Set Bold formatting to the selected content 
        private void btnBold_Click(object sender, RoutedEventArgs e)
        {
            if (rtb != null && rtb.Selection.Text.Length > 0)
            {
                if (rtb.Selection.GetPropertyValue(Run.FontWeightProperty) is FontWeight && ((FontWeight)rtb.Selection.GetPropertyValue(Run.FontWeightProperty)) == FontWeights.Normal)
                    rtb.Selection.ApplyPropertyValue(Run.FontWeightProperty, FontWeights.Bold);
                else
                    rtb.Selection.ApplyPropertyValue(Run.FontWeightProperty, FontWeights.Normal);
            }
            ReturnFocus();
        }

        //Set Italic formatting to the selected content 
        private void btnItalic_Click(object sender, RoutedEventArgs e)
        {
            if (rtb != null && rtb.Selection.Text.Length > 0)
            {
                if (rtb.Selection.GetPropertyValue(Run.FontStyleProperty) is System.Windows.FontStyle && ((System.Windows.FontStyle)rtb.Selection.GetPropertyValue(Run.FontStyleProperty)) == FontStyles.Normal)
                    rtb.Selection.ApplyPropertyValue(Run.FontStyleProperty, FontStyles.Italic);
                else
                    rtb.Selection.ApplyPropertyValue(Run.FontStyleProperty, FontStyles.Normal);
            }
            ReturnFocus();
        }

        //Set Underline formatting to the selected content 
        private void btnUnderline_Click(object sender, RoutedEventArgs e)
        {
            if (rtb != null && rtb.Selection.Text.Length > 0)
            {
                if (rtb.Selection.GetPropertyValue(Run.TextDecorationsProperty) == null)
                    rtb.Selection.ApplyPropertyValue(Run.TextDecorationsProperty, TextDecorations.Underline);
                else
                    rtb.Selection.ApplyPropertyValue(Run.TextDecorationsProperty, null);
            }
            ReturnFocus();

        }
        #endregion 

        #region Font Type, Color & size 

        //Set font type to selected content
        private void cmbFonts_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (rtb != null && rtb.Selection.Text.Length > 0)
            {
                rtb.Selection.ApplyPropertyValue(Run.FontFamilyProperty, new FontFamily((cmbFonts.SelectedItem as ComboBoxItem).Tag.ToString()));
            }
            ReturnFocus();
        }

        //Set font size to selected content
        private void cmbFontSizes_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (rtb != null && rtb.Selection.Text.Length > 0)
            {
                rtb.Selection.ApplyPropertyValue(Run.FontSizeProperty, double.Parse((cmbFontSizes.SelectedItem as ComboBoxItem).Tag.ToString()));
            }
            ReturnFocus();
        }

        //Set font color to selected content
        private void cmbFontColors_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (rtb != null && rtb.Selection.Text.Length > 0)
            {
                string color = (cmbFontColors.SelectedItem as ComboBoxItem).Tag.ToString();

                SolidColorBrush brush = new SolidColorBrush(Color.FromArgb(
                    byte.Parse(color.Substring(0, 2), System.Globalization.NumberStyles.HexNumber),
                    byte.Parse(color.Substring(2, 2), System.Globalization.NumberStyles.HexNumber),
                    byte.Parse(color.Substring(4, 2), System.Globalization.NumberStyles.HexNumber),
                    byte.Parse(color.Substring(6, 2), System.Globalization.NumberStyles.HexNumber)));

                rtb.Selection.ApplyPropertyValue(Run.ForegroundProperty, brush);
            }
            ReturnFocus();
        }
        #endregion 

        #region createImageFromUri
        private static Image createImageFromUri(Uri URI, double width, double height)
        {
            Image img = new Image();
            img.Stretch = Stretch.Uniform;
            img.Width = width;
            img.Height = height;
            BitmapImage bi = new BitmapImage(URI);
            img.Source = bi;
            img.Tag = bi.UriSource.ToString();
            return img;
        } 
        #endregion

        #region Insert Hyperlink

        //Insert a hyperlink
        private void btnHyperlink_Click(object sender, RoutedEventArgs e)
        {
            InsertURL cw = new InsertURL(rtb.Selection.Text);
            cw.HasCloseButton = false;
            
            //Hook up an event handler to the Closed event on the ChildWindows cw. 
            cw.Closed += (s, args) =>
            {
                if (cw.DialogResult.Value)
                {
                    Hyperlink hyperlink = new Hyperlink();
                    hyperlink.TargetName = "_blank";
                    hyperlink.NavigateUri = new Uri(cw.txtURL.Text);

                    if (cw.txtURLDesc.Text.Length > 0)
                        hyperlink.Inlines.Add(cw.txtURLDesc.Text);
                    else
                        hyperlink.Inlines.Add(cw.txtURL.Text);

                    rtb.Selection.Insert(hyperlink);
                    ReturnFocus();
                }
            };
            cw.Show();
        }
        #endregion 

        #region Clipboard Operations

        //Cut the selected text
        private void btnCut_Click(object sender, RoutedEventArgs e)
        {
            Clipboard.SetText(rtb.Selection.Text);
            rtb.Selection.Text = "";
            ReturnFocus();
        }

        //Copy the selected text
        private void btnCopy_Click(object sender, RoutedEventArgs e)
        {
            Clipboard.SetText(rtb.Selection.Text);
            ReturnFocus();
        }

        //paste the text
        private void btnPaste_Click(object sender, RoutedEventArgs e)
        {
            rtb.Selection.Text = Clipboard.GetText();
            ReturnFocus();
        }
        #endregion 

        #region Print

        //Print the document
        private void btnPrint_Click(object sender, RoutedEventArgs e)
        {
            PrintPreview cw = new PrintPreview();
            cw.ShowPreview(rtb);
            cw.HasCloseButton = false;

            //Hook up a handler to the Closed event before we display the PrintPreview window by calling the Show() method.
            cw.Closed += (t, a) =>
            {
                if (cw.DialogResult.Value)
                {
                    PrintDocument theDoc = new PrintDocument();
                    theDoc.PrintPage += (s, args) =>
                    {
                        args.PageVisual = rtb;
                        args.HasMorePages = false;
                    };

                    theDoc.EndPrint += (s, args) =>
                    {
                        MessageBox.Show("The document printed successfully", "Text Editor", MessageBoxButton.OK);
                    };

                    theDoc.Print("Silverlight 4 Text Editor");
                    ReturnFocus();
                }
            };
            cw.Show();
        }

        #endregion 

        

        

        #region Context Menu

        //Though we dont execute any logic on Right Mouse button down, we need to ensure the event is set to be handled to allow
        //the subsequent Right Mouse button up to be raised on the control. The context menu is displayed when the right mouse
        //button up event is raised. 
        private void rtb_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
        {
            e.Handled = true;
        }

        private void rtb_MouseRightButtonUp(object sender, MouseButtonEventArgs e)
        {
            //Construct and display the context menu

            RTBContextMenu menu = new RTBContextMenu(rtb);
            menu.Show(e.GetPosition(LayoutRoot));
        }
        #endregion 

        #region Highlight

        private List<Rect> m_selectionRect = new List<Rect>();

      

        Rectangle highlightRect;
        MouseEventArgs lastRTBMouseMove;
        private void rtb_MouseMove(object sender, MouseEventArgs e)
        {
           
        }

        private Rectangle CreateHighlightRectangle(Rect bounds)
        {
            Rectangle r = new Rectangle();
            r.Fill = new SolidColorBrush(Color.FromArgb(75, 0, 0, 200));
            r.Stroke = new SolidColorBrush(Color.FromArgb(230, 0, 0, 254));
            r.StrokeThickness = 1;
            r.Width = bounds.Width;
            r.Height = bounds.Height;
            Canvas.SetLeft(r, bounds.Left);
            Canvas.SetTop(r, bounds.Top);

            highlightCanvas.Children.Add(r);

            return r;

        }
        #endregion 

        #region DragAndDrop

        private void rtb_Drop(object sender, System.Windows.DragEventArgs e)
        {
            VisualStateManager.GoToState(this, "Normal", true);

            //the Drop event passes in an array of FileInfo objects for the list of files that were selected and drag-dropped onto the RichTextBox.
            if (e.Data == null)
            {
                ReturnFocus();
                return;
            }

            //This checks if the dropped objects are files and if not, return. 
            IDataObject f = e.Data as IDataObject;

            if (f == null)
            {
                ReturnFocus();
                return;
            }

            object data = f.GetData(DataFormats.FileDrop);
            FileInfo[] files = data as FileInfo[];

            if (files == null)
            {
                ReturnFocus();
                return;
            }

            //Walk through the list of FileInfo objects of the selected and drag-dropped files and parse the .txt and .docx files 
            //and insert their content in the RichTextBox.
            foreach (FileInfo file in files)
            {
                if (file == null)
                {
                    continue;
                }

                if (file.Extension.Equals(".txt"))
                {
                    ParseTextFile(file);
                }
                else if (file.Extension.Equals(".docx"))
                {
                    ParseDocxFile(file);
                }
            }
            ReturnFocus();
        }

        //Create a StreamReader on the text file and read as a string. 
        private void ParseTextFile(FileInfo file)
        {
            Stream sr = file.OpenRead();
            string contents;
            using (StreamReader reader = new StreamReader(sr))
            {
                contents = reader.ReadToEnd();
            }

            rtb.Selection.Text = contents;
            sr.Close();                 
        }

        private void ParseDocxFile(FileInfo file)
        {
            Stream sr = file.OpenRead();
            string contents;

            StreamResourceInfo zipInfo = new StreamResourceInfo(sr, null);
            StreamResourceInfo wordInfo = Application.GetResourceStream(zipInfo, new Uri("word/document.xml", UriKind.Relative));

            using (StreamReader reader = new StreamReader(wordInfo.Stream))
            {
                contents = reader.ReadToEnd();
            }

            XDocument xmlFile = XDocument.Parse(contents);
            XNamespace w = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";

            var query = from xp in xmlFile.Descendants(w + "p")
                        select xp;
            Paragraph p = null;
            Run r = null;
            foreach (XElement xp in query)
            {
                p = new Paragraph();
                var query2 = from xr in xp.Descendants(w + "r")
                             select xr;
                foreach (XElement xr in query2)
                {
                    r = new Run();
                    var query3 = from xt in xr.Descendants()
                                 select xt;
                    foreach (XElement xt in query3)
                    {
                        if (xt.Name == (w + "t"))
                            r.Text = xt.Value.ToString();
                        else if (xt.Name == (w + "br"))
                            p.Inlines.Add(new LineBreak());
                    }
                    p.Inlines.Add(r);
                }
                p.Inlines.Add(new LineBreak());
                rtb.Blocks.Add(p);
            }
        }

        private void rtb_DragEnter(object sender, System.Windows.DragEventArgs e)
        {
            VisualStateManager.GoToState(this, "DragOver", true);
        }

        private void rtb_DragLeave(object sender, System.Windows.DragEventArgs e)
        {
            VisualStateManager.GoToState(this, "Normal", true);        
        }

        #endregion 

        #region FileOperations
         //Saves the existing file
        private void btnSave_Click(object sender, RoutedEventArgs e)
        {
            string ContentToSave = rtb.Xaml;

            //Check if the file contains any UIElements
            var res = from block in rtb.Blocks
                      from inline in (block as Paragraph).Inlines
                      where inline.GetType() == typeof(InlineUIContainer)
                      select inline;

            //If the file contains any UIElements, it will not be saved
            if (res.Count() != 0)
            {
                MessageBox.Show("Saving documents with UIElements is not supported");
                return;
            }

            SaveFileDialog sfd = new SaveFileDialog();
            sfd.DefaultExt = ".sav";
            sfd.Filter = "Saved Files|*.sav|All Files|*.*";

            if (sfd.ShowDialog().Value)
            {
                using (FileStream fs = (FileStream)sfd.OpenFile())
                {
                    System.Text.UTF8Encoding enc = new System.Text.UTF8Encoding();
                    byte[] buffer = enc.GetBytes(ContentToSave);
                    fs.Write(buffer, 0, buffer.Length);
                    fs.Close();
                }
            }
        }

        //Opens an existing file
        private void btnOpen_Click(object sender, RoutedEventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog();
            ofd.Multiselect = false;
            ofd.Filter = "Saved Files|*.sav|All Files|*.*";

            if (ofd.ShowDialog().Value)
            {
                FileInfo fi = ofd.File;
                StreamReader r = fi.OpenText();
                rtb.Xaml = r.ReadToEnd();
                r.Close();
            }
        }
        #endregion

        #region helper functions

        private void ReturnFocus()
        {
            if (rtb != null)
                rtb.Focus();
        }
        #endregion 

        private void UserControl_Loaded(object sender, RoutedEventArgs e)
        {

        }
    }
}
